package org.mall.modules.mq.pay;

import com.alibaba.fastjson.JSON;
import org.apache.commons.collections.CollectionUtils;
import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyContext;
import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyStatus;
import org.apache.rocketmq.client.consumer.listener.MessageListenerConcurrently;
import org.apache.rocketmq.common.message.MessageExt;
import org.mall.common.constant.PayTransactionNotifyStatusEnum;
import org.mall.common.util.DateUtil;
import org.mall.common.util.json.JacksonUtils;
import org.mall.framework.util.ExceptionUtil;
import org.mall.modules.order.service.OrderService;
import org.mall.modules.pay.dataobject.PayNotifyLogDO;
import org.mall.modules.pay.dataobject.PayNotifyTaskDO;
import org.mall.modules.pay.dataobject.PayTransactionDO;
import org.mall.modules.pay.mapper.PayNotifyLogMapper;
import org.mall.modules.pay.mapper.PayNotifyTaskMapper;
import org.mall.modules.pay.mapper.PayTransactionMapper;
import org.mall.modules.pojo.pay.message.PayTransactionSuccessMessage;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

import javax.annotation.Resource;
import java.util.Calendar;
import java.util.Date;
import java.util.List;

@Component
public class PayTransactionSuccessLister   implements MessageListenerConcurrently {

    @Autowired
    private PayTransactionMapper payTransactionMapper;
    @Autowired
    private OrderService orderService;
    @Resource
    private PayNotifyTaskMapper payTransactionNotifyTaskMapper;
    @Resource
    private PayNotifyLogMapper payTransactionNotifyLogMapper;
    protected String invoke(PayTransactionSuccessMessage message) {
        // 查询支付交易
        PayTransactionDO transaction = payTransactionMapper.selectById(message.getTransactionId());
        Assert.notNull(transaction, String.format("回调消息(%s) 订单交易不能为空", message.toString()));
        // 执行调用
        return orderService.updatePaySuccess(Long.valueOf(message.getOrderId()),transaction.getPrice());
    }

    protected void afterInvokeSuccess(PayTransactionSuccessMessage message) {
        PayTransactionDO updateTransaction = new PayTransactionDO().setId(message.getTransactionId()).setFinishTime(new Date());
        payTransactionMapper.update(updateTransaction, null);
    }

    @Override
    @Transactional
    public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> list, ConsumeConcurrentlyContext context) {
        if (CollectionUtils.isEmpty(list)){
            return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
        }
        MessageExt messageExt = list.get(0);
        PayTransactionSuccessMessage message= (PayTransactionSuccessMessage) JacksonUtils.parseObject(new String(messageExt.getBody()), PayTransactionSuccessMessage.class);
        String response = null;
        PayNotifyTaskDO updateTask = new PayNotifyTaskDO()
                .setId(message.getId())
                .setLastExecuteTime(new Date())
                .setNotifyTimes(message.getNotifyTimes() + 1);
        try {
            response = invoke(message);
            if ("success".equals(response)) {
                // 情况一，请求成功且返回成功
                // 更新通知成功
                updateTask.setStatus(PayTransactionNotifyStatusEnum.SUCCESS.getValue());
                payTransactionNotifyTaskMapper.update(updateTask);
                // 需要更新支付交易单通知应用成功
                afterInvokeSuccess(message);
            } else { // 情况二，请求成功且返回失败
                // 更新通知请求成功，但是结果失败
                handleFailure(updateTask, PayTransactionNotifyStatusEnum.REQUEST_SUCCESS.getValue());
                payTransactionNotifyTaskMapper.update(updateTask);
            }
        } catch (Throwable e) { // 请求失败
            // 更新通知请求失败
            response = ExceptionUtil.getRootCauseMessage(e);
            handleFailure(updateTask, PayTransactionNotifyStatusEnum.REQUEST_FAILURE.getValue());
            payTransactionNotifyTaskMapper.update(updateTask);
            // 抛出异常，回滚事务
            throw e;
            // TODO 芋艿，此处不能抛出异常。因为，会导致 MQ + 定时任务多重试。此处的目标是，事务回滚 + 吃掉事务。另外，最后的 finally 的日志，要插入成功。
        } finally {
            // 插入 PayTransactionNotifyLogDO 日志
            PayNotifyLogDO notifyLog = new PayNotifyLogDO().setNotifyId(message.getId())
                    .setRequest(JSON.toJSONString(message)).setResponse(response).setStatus(updateTask.getStatus());
            payTransactionNotifyLogMapper.insert(notifyLog);
        }


        return null;
    }

    void handleFailure(PayNotifyTaskDO updateTask, Integer defaultStatus) {
        if (updateTask.getNotifyTimes() >= PayNotifyTaskDO.NOTIFY_FREQUENCY.length) {
            updateTask.setStatus(PayTransactionNotifyStatusEnum.FAILURE.getValue());
        } else {
            updateTask.setNextNotifyTime(DateUtil.addDate(Calendar.SECOND, PayNotifyTaskDO.NOTIFY_FREQUENCY[updateTask.getNotifyTimes()]));
            updateTask.setStatus(defaultStatus);
        }
    }
}
